package cn.edu.nuaa.computer.utils;


/**
 * 表达式工具类
 */
public class ExpressionUtils {
    public static int mc = 0;

    /**
     * 将表达式字符串转换为二叉树
     * 左符号，右数字
     *
     * @param exStr 表达式字符串
     * @return 逆波兰表达式的二叉树
     */
    public static TreeStack getTreeFromString(String exStr) {
        if (isLegal(exStr)) {
            TreeStack treeStack = new TreeStack();
            // 如果表达式被括号包裹
            if (isBracket(exStr)) {
                exStr = exStr.substring(1, exStr.length() - 1);
            }
            // 判断是否为ln,sin,cos,tan,求根运算
            if ((exStr.charAt(0) == 'l' || exStr.charAt(0) == 's' || exStr.charAt(0) == 'c' || exStr.charAt(0) == 't' || exStr.charAt(0) == 'g') && (isPower(exStr.substring(1)) || isLegal(exStr.substring(1)))) {
                if (exStr.charAt(1) != '(' && (exStr.length() == 2 || isNum(exStr.substring(1)))) {
                    // 如果是lnx，则直接将lnx存入
                    Double of = Double.valueOf(exStr.substring(1));
                    double v = 0;
                    switch (exStr.charAt(0)) {
                        case 'l' -> {
                            v = Math.log(of);
                        }
                        case 's' -> {
                            v = Math.sin(of);
                        }
                        case 'c' -> {
                            v = Math.cos(of);
                        }
                        case 't' -> {
                            v = Math.tan(of);
                        }
                        case 'g' -> {
                            v = Math.sqrt(of);
                        }
                    }
                    treeStack.setData(new MyData(v));
                    return treeStack;
                } else if (exStr.charAt(1) == '(') {
                    if (isPower(exStr.substring(1))) {
                        treeStack.setData(new MyData(exStr.charAt(0)));
                        treeStack.setLeftNode(getTreeFromString(exStr.substring(1)));
                        return treeStack;
                    }
                    int n = 0, i;
                    for (i = 1; i < exStr.toCharArray().length; i++) {
                        char c = exStr.charAt(i);
                        if (c == '(') {
                            n++;
                        }
                        if (c == ')') {
                            n--;
                        }
                        if (n == 0) {
                            break;
                        }
                    }
                    if (i == exStr.toCharArray().length - 1) {
                        treeStack.setData(new MyData(exStr.charAt(0)));
                        treeStack.setLeftNode(getTreeFromString(exStr.substring(1)));
                        return treeStack;
                    }
                }
            }
            // 判断是否为幂次方运算
            if (isPower(exStr)) {
                treeStack.setData(new MyData('^'));
                treeStack.setLeftNode(getTreeFromString(exStr.substring(0, mc)));
                treeStack.setRightNode(getTreeFromString(exStr.substring(mc + 1)));
                mc = 0;
                return treeStack;
            }
            // 如果字符串为纯数字
            if (isNum(exStr)) {
                MyData myData = new MyData(Double.valueOf(exStr));
                treeStack.setData(myData);
            } else {
                // 用于存放数据
                StringBuffer s1 = new StringBuffer();
                StringBuffer s2 = new StringBuffer();
                // 用于计算括号的数量
                int n = 0;
                // 是否有+-
                boolean isAddOrSub = isAddAndSub(exStr);
                // 判断左节点还是右节点
                boolean s1OrS2 = true;
                // 判断符号是否已取出
                boolean symbol = false;
                for (int i = 0; i < exStr.length(); i++) {
                    char c = exStr.charAt(i);
                    if (n == 0) {
                        if (!symbol) {
                            if (c == '(') {
                                n++;
                            }
                            if (c == '+' || c == '-' || c == '*' || c == '/') {
                                if (isAddOrSub) {
                                    if (c == '+' || c == '-') {
                                        MyData myData = new MyData(c);
                                        treeStack.setData(myData);
                                        s1OrS2 = false;
                                        symbol = true;
                                        continue;
                                    }
                                } else {
                                    MyData myData = new MyData(c);
                                    treeStack.setData(myData);
                                    s1OrS2 = false;
                                    symbol = true;
                                    continue;
                                }
                            }
                        }
                    } else {
                        if (c == '(') {
                            n++;
                        }
                        if (c == ')') {
                            n--;
                        }
                    }
                    if (s1OrS2) {
                        s1.append(c);
                    } else {
                        s2.append(c);
                    }
                }
                treeStack.setLeftNode(getTreeFromString(s1.toString()));
                treeStack.setRightNode(getTreeFromString(s2.toString()));
            }
            return treeStack;
        }
        throw new RuntimeException("字符串不合法");
    }

    /**
     * 从二叉树表达式计算结果
     *
     * @param treeStack 二叉树表达式
     * @return 计算出的数值
     */
    public static Double getBearFromTree(TreeStack treeStack) {
        if (treeStack.getData().getNum() != null) {
            return treeStack.getData().getNum();
        }
        Character symbol = treeStack.getData().getSymbol();
        if (symbol == '*') {
            return getBearFromTree(treeStack.getLeftNode()) * getBearFromTree(treeStack.getRightNode());
        }
        if (symbol == '/') {
            return getBearFromTree(treeStack.getLeftNode()) / getBearFromTree(treeStack.getRightNode());
        }
        if (symbol == '+') {
            return getBearFromTree(treeStack.getLeftNode()) + getBearFromTree(treeStack.getRightNode());
        }
        if (symbol == '-') {
            return getBearFromTree(treeStack.getLeftNode()) - getBearFromTree(treeStack.getRightNode());
        }
        if (symbol == 'l') {
            return Math.log(getBearFromTree(treeStack.getLeftNode()));
        }
        if (symbol == 's') {
            return Math.sin(getBearFromTree(treeStack.getLeftNode()));
        }
        if (symbol == 'c') {
            return Math.cos(getBearFromTree(treeStack.getLeftNode()));
        }
        if (symbol == 't') {
            return Math.tan(getBearFromTree(treeStack.getLeftNode()));
        }
        if (symbol == '^') {
            return Math.pow(getBearFromTree(treeStack.getLeftNode()), getBearFromTree(treeStack.getRightNode()));
        }
        if (symbol == 'g') {
            return Math.sqrt(getBearFromTree(treeStack.getLeftNode()));
        }
        return 0.0;
    }

    // 判断是否为幂次方运算
    public static boolean isPower(String s) {
        boolean b = false;
        int a;
        for (a = 0; a < s.length(); a++) {
            char c = s.charAt(a);
            if (c == '^' && isLegal(s.substring(0, a)) && isLegal(s.substring(a + 1))) {
                b = true;
                mc = a;
                break;
            }
        }
        if (b) {
            if (s.charAt(0) == '(') {
                int n = 0;
                int i;
                for (i = 0; i < s.toCharArray().length; i++) {
                    char c = s.charAt(i);
                    if (c == '(') {
                        n++;
                    }
                    if (c == ')') {
                        n--;
                    }
                    if (n == 0) {
                        break;
                    }
                }
                i++;
                if (i == a) {
                    return isPowerUtil(s, a + 1);
                } else {
//                    return isNum(s.substring(i + 1));
                    return false;
                }
            } else {
                if (isNum(s.substring(0, a))) {
                    return isPowerUtil(s, a + 1);
                } else {
//                    return isNum(s.substring(a + 1));
                    return false;
                }
            }
        }
        return false;
    }

    private static boolean isPowerUtil(String s, int a) {
        int n = 0;
        if (s.charAt(a) == '(') {
            int i;
            for (i = a; i < s.toCharArray().length; i++) {
                char c = s.charAt(i);
                if (c == '(') {
                    n++;
                }
                if (c == ')') {
                    n--;
                }
                if (n == 0) {
                    break;
                }
            }
            return i == s.toCharArray().length - 1;
        }
        return isNum(s.substring(a));
    }

    // 判断表达式是否合法
    private static boolean isLegal(String s) {
        // 判断字符串是否全为数学符号
        {
            for (char c : s.toCharArray()) {
                if (!((c >= '0' && c <= '9') || isSymbol(c) || c == '(' || c == ')' || c == '.' || c == 'e' || c == 'p' || c == 's' || c == 'c' || c == 't' || c == 'g' || c == '^' || c == 'l')) {
                    return false;
                }
            }
        }
        // 判断括号
        {
            // 括号数量
            int a = 0;
            for (char c : s.toCharArray()) {
                if (a < 0) {
                    return false;
                }
                if (c == '(') {
                    a++;
                }
                if (c == ')') {
                    a--;
                }
            }
            if (a != 0) {
                return false;
            }
        }
        // 判断符号
        {
            if (isSymbol(s.charAt(0)) || isSymbol(s.charAt(s.length() - 1))) {
                return false;
            }
            for (int i = 1; i < s.length() - 1; i++) {
                char c = s.charAt(i);
                if (isSymbol(s.charAt(i)) && isSymbol(s.charAt(i + 1))) {
                    return false;
                }
                boolean b1 = !isSymbol(s.charAt(i)) && s.charAt(i) != '(' && (s.charAt(i + 1) == '(') && !(c == 's' || c == 'c' || c == 't' || c == 'g' || c == '^' || c == 'l');
                boolean b2 = !isSymbol(s.charAt(i)) && s.charAt(i) == ')' && (((s.charAt(i + 1) <= '9' && s.charAt(i + 1) >= '0') || s.charAt(i + 1) == 'p' || s.charAt(i + 1) == 'e') || s.charAt(i + 1) == '(');
                if ((b1) || (b2)) {
                    return false;
                }
            }
        }
        return true;
    }

    private static boolean isAddAndSub(String s) {
        char[] chars = s.toCharArray();
        int n = 0;
        for (char c : chars) {
            if (n == 0) {
                if (c == '(') {
                    n++;
                    continue;
                }
                if (c == '+' || c == '-') {
                    return true;
                }
            } else {
                if (c == '(') {
                    n++;
                }
                if (c == ')') {
                    n--;
                }
            }
        }
        return false;
    }

    // 判断字符是否为符号
    private static boolean isSymbol(char c) {
        return (c == '+' || c == '-' || c == '*' || c == '/');
    }

    // 判断字符串是否为纯数字
    private static boolean isNum(String s) {
        for (char c : s.toCharArray()) {
            if ((c < '0' || c > '9') && c != '.' && c != 'e' && c != 'p') {
                return false;
            }
        }
        return true;
    }

    // 判断表达式是否被括号包裹
    private static boolean isBracket(String s) {
        boolean a = false;
        // 如果左右都有括号则成立
        if (s.charAt(0) == '(' && s.charAt(s.length() - 1) == ')') {
            // 记录括号的数量
            int n = 1;
            for (int i = 1; i < s.length(); i++) {
                if (n == 0) {
                    return a;
                }
                if (s.charAt(i) == '(') {
                    n++;
                }
                if (s.charAt(i) == ')') {
                    n--;
                }
            }
            a = true;
        }
        return a;
    }

    // 判断是否为ln,sin,cos,tan
    public static boolean isSpecialSymbol(char c) {
        return (c == 'l' || c == 's' || c == 'c' || c == 't');
    }

    // 将字符串中的p和e进行替换
    public static String replacePiAndE(String ext) {
        double pi = Math.PI;
        double e = Math.E;
        ext = ext.replace("p", Double.toString(pi));
        ext = ext.replace("e", Double.toString(e));
        return ext;
    }


    public static void main(String[] args) {
        String a = "g(1+2^1)";
        a = replacePiAndE(a);
        TreeStack treeStack = getTreeFromString(a);
        System.out.println(treeStack);
        Double bear = getBearFromTree(treeStack);
        bear = Double.valueOf(String.format("%.2f", bear));
        System.out.println(a + " = " + bear);
    }
}
